home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 423_01 / rdcf / rdcf2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-14  |  53.9 KB  |  1,636 lines

  1. /*-----------------------------------------------------------------------------
  2.           RDCF: A Reentrant DOS-Compatible File System, Version 2.0
  3.  
  4.                             by Philip J. Erdelsky
  5.                           PLAIN VANILLA CORPORATION
  6.                             CompuServe 75746,3411
  7.                       InterNet 75746.3411@compuserve.com
  8.  
  9.                                January 15, 1993
  10.  
  11.                  Copyright (c) 1993 Plain Vanilla Corporation
  12. -----------------------------------------------------------------------------*/
  13.  
  14. /* #define _BIG_ENDIAN */
  15.  
  16. #include "rdcf2.h"
  17. #include <mem.h>
  18. #include <ctype.h>
  19. #include <stdio.h>
  20.  
  21. #define NAME_SIZE       8
  22. #define EXTENSION_SIZE  3
  23.  
  24. /* Directory structure as it appears on the diskette. */
  25.  
  26. struct directory
  27. {
  28.   unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
  29.   unsigned char attribute;
  30.   unsigned char reserved[10];
  31.   unsigned short time;
  32.   unsigned short date;
  33.   unsigned short first_cluster;
  34.   unsigned long size;
  35. };
  36.  
  37. #ifdef RDCF_SECTOR_SIZE
  38.   #define SECTOR_SIZE RDCF_SECTOR_SIZE
  39. #else
  40.   #define SECTOR_SIZE f->sector_size
  41. #endif
  42.  
  43. #define ENTRIES_PER_SECTOR  (SECTOR_SIZE/sizeof(struct directory))
  44. #define NO_DIRECTORY_INDEX  0xFFFF
  45.  
  46. /* Special values for first byte of name. */
  47.  
  48. #define END_DIRECTORY     0x00
  49. #define DELETED_FILE      0xE5
  50.  
  51. /* Special values for FAT entries. */
  52.  
  53. #define EMPTY_CLUSTER            0
  54. #define RESERVED_CLUSTER_12_BIT  0xFF0
  55. #define LAST_CLUSTER_12_BIT      0xFF8
  56. #define LAST_CLUSTER_16_BIT      0xFFF8
  57.  
  58. /* buffer status */
  59.  
  60. enum buffer_status {EMPTY, CLEAN, DIRTY};
  61.  
  62. /* additional mode bit */
  63.  
  64. #define WRITTEN    (1<<7)
  65.  
  66. /*-----------------------------------------------------------------------------
  67. Layout of first sector when read into buffer[].  A simple structure would not
  68. be portable because some words are not aligned on even addresses.
  69. -----------------------------------------------------------------------------*/
  70.  
  71. #define BYTES_PER_SECTOR     (buffer[11]|buffer[12]<<8)
  72. #define SECTORS_PER_CLUSTER   buffer[13]
  73. #define RESERVED_SECTORS     (buffer[14]|buffer[15]<<8)
  74. #define NUMBER_OF_FATS        buffer[16]
  75. #define ROOT_ENTRIES         (buffer[17]|buffer[18]<<8)
  76. #define TOTAL_SECTORS        (buffer[19]|buffer[20]<<8)
  77. #define MEDIA_DESCRIPTOR      buffer[21]
  78. #define SECTORS_PER_FAT      (buffer[22]|buffer[23]<<8)
  79. #define SECTORS_PER_TRACK    (buffer[24]|buffer[25]<<8)
  80. #define HEADS                (buffer[26]|buffer[27]<<8)
  81. #define HIDDEN_SECTORS       (buffer[28]|buffer[29]<<8)
  82.  
  83. /*-----------------------------------------------------------------------------
  84. The following functions and macros convert words and double words from "big
  85. endian" to "little endian" form or vice-versa.
  86. -----------------------------------------------------------------------------*/
  87.  
  88. #ifdef _BIG_ENDIAN
  89.  
  90.   static void swap_two(unsigned char *p)
  91.   {
  92.     unsigned char x = p[0];
  93.     p[0] = p[1];
  94.     p[1] = x;
  95.   }
  96.  
  97.   static void swap_four(unsigned char *p)
  98.   {
  99.     unsigned char x = p[0];
  100.     p[0] = p[3];
  101.     p[3] = x;
  102.     swap_two(p+1);
  103.   }
  104.  
  105.   #define convert_short(x) swap_two((unsigned char *)(&(x)))
  106.   #define convert_long(x)  swap_four((unsigned char *)(&(x)))
  107.  
  108. #endif
  109.  
  110. /*-----------------------------------------------------------------------------
  111. This function calls longjmp() to specify an error code and exit from the RDCF
  112. function originally called.
  113. -----------------------------------------------------------------------------*/
  114.  
  115. static void error_exit(struct rdcf *f, int error)
  116. {
  117.   longjmp(f->error, error);
  118. }
  119.  
  120. /*-----------------------------------------------------------------------------
  121. This function reads or writes a sector and bails out if the function
  122. (*drive_access)() returns an error.
  123. -----------------------------------------------------------------------------*/
  124.  
  125. static void access_sector(struct rdcf *f, int write, unsigned sector,
  126.   void *buffer)
  127. {
  128.   f->drive_error = (*f->drive_access)(write, f->drive, sector, buffer);
  129.   if (f->drive_error != 0)
  130.     error_exit(f, RDCF_DRIVE_ERROR);
  131. }
  132.  
  133. /*-----------------------------------------------------------------------------
  134. These macros make the calls on access_sector() more readable.
  135. -----------------------------------------------------------------------------*/
  136.  
  137. #define read_sector(f,s,b)   access_sector(f,0,s,b)
  138. #define write_sector(f,s,b)  access_sector(f,1,s,b)
  139.  
  140. /*-----------------------------------------------------------------------------
  141. This function writes the buffer in the FCB if it is marked as "dirty".
  142. -----------------------------------------------------------------------------*/
  143.  
  144. #ifndef RDCF_SYSTEM_READ_ONLY
  145.  
  146.   static void flush_buffer(struct rdcf *f)
  147.   {
  148.     if (f->buffer_status == DIRTY)
  149.     {
  150.       write_sector(f, f->sector_in_buffer, f->buffer);
  151.       f->buffer_status = CLEAN;
  152.     }
  153.   }
  154.  
  155. #endif
  156.  
  157. /*-----------------------------------------------------------------------------
  158. This function reads a sector into the buffer in the FCB, if it is not already
  159. there.  If another sector is there, the buffer is first flushed.
  160. -----------------------------------------------------------------------------*/
  161.  
  162. static void read_buffer(struct rdcf *f, unsigned sector)
  163. {
  164.   if (f->buffer_status == EMPTY || sector != f->sector_in_buffer)
  165.   {
  166.     #ifndef RDCF_SYSTEM_READ_ONLY
  167.       flush_buffer(f);
  168.     #endif
  169.     read_sector(f, sector, f->buffer);
  170.     f->sector_in_buffer = sector;
  171.     f->buffer_status = CLEAN;
  172.   }
  173. }
  174.  
  175. /*-----------------------------------------------------------------------------
  176. This function checks to see if a cluster number is valid and declares an error
  177. if it is not.
  178. -----------------------------------------------------------------------------*/
  179.  
  180. static void check_cluster(struct rdcf *f, unsigned cluster)
  181. {
  182.   if (cluster < 2 || cluster > f->maximum_cluster_number)
  183.     error_exit(f, RDCF_FILE_FORMAT_ERROR);
  184. }
  185.  
  186. /*-----------------------------------------------------------------------------
  187. This function returns the FAT entry for the specified cluster.
  188. -----------------------------------------------------------------------------*/
  189.  
  190. static unsigned FAT_entry(struct rdcf *f, unsigned cluster)
  191. {
  192.   check_cluster(f, cluster);
  193.   if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT)
  194.   {
  195.     unsigned byte_index = cluster + (cluster>>1);
  196.     unsigned char p[2];
  197.     read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE);
  198.     p[0] = f->buffer[byte_index%SECTOR_SIZE];
  199.     byte_index++;
  200.     read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE);
  201.     p[1] = f->buffer[byte_index%SECTOR_SIZE];
  202.     return cluster&1 ? p[1]<<4 | p[0]>>4 : p[0] | p[1]<<8&0xF00;
  203.   }
  204.   else
  205.   {
  206.     unsigned short x;
  207.     read_buffer(f, f->first_FAT_sector + cluster/(SECTOR_SIZE/2));
  208.     x = ((unsigned short *)(f->buffer))[cluster%(SECTOR_SIZE/2)];
  209.     #ifdef _BIG_ENDIAN
  210.       convert_short(x);
  211.     #endif
  212.     return x;
  213.   }
  214. }
  215.  
  216. /*-----------------------------------------------------------------------------
  217. This function sets the FAT entry for the specified cluster to the specified
  218. value.  The 12-bit FAT entry always occupies two consecutive bytes, filling one
  219. byte completely and filling only one nibble of the other byte.  Since these
  220. bytes may be in different sectors, two separate calls on read_buffer() are
  221. used.  The one-sector caching implemented by read_buffer() prevents multiple
  222. disk accesses when both bytes are in the same sector.  Every copy of the FAT is
  223. updated in the same way.
  224. -----------------------------------------------------------------------------*/
  225.  
  226. #ifndef RDCF_SYSTEM_READ_ONLY
  227.  
  228.   static void set_FAT_entry(struct rdcf *f, unsigned cluster, unsigned x)
  229.   {
  230.     unsigned sector;
  231.     check_cluster(f, cluster);
  232.     #ifdef _BIG_ENDIAN
  233.       if (f->maximum_cluster_number >= RESERVED_CLUSTER_12_BIT)
  234.         convert_short(x);
  235.     #endif
  236.     for (sector = f->first_FAT_sector; sector < f->first_directory_sector;
  237.       sector += f->sectors_per_FAT)
  238.     {
  239.       if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT)
  240.       {
  241.         unsigned byte_index = cluster + (cluster>>1);
  242.         unsigned char *p;
  243.         read_buffer(f, sector + byte_index/SECTOR_SIZE);
  244.         p = f->buffer + byte_index%SECTOR_SIZE;
  245.         *p = cluster&1 ? *p&0x0F | x<<4 : x;
  246.         f->buffer_status = DIRTY;
  247.         read_buffer(f, sector + (byte_index+1)/SECTOR_SIZE);
  248.         p = f->buffer + (byte_index+1)%SECTOR_SIZE;
  249.         *p = cluster&1 ? x>>4 : *p&0xF0 | x>>8;
  250.       }
  251.       else
  252.       {
  253.         read_buffer(f, sector + cluster/(SECTOR_SIZE/2));
  254.         ((unsigned short *)(f->buffer))[cluster%(SECTOR_SIZE/2)] = x;
  255.       }
  256.       f->buffer_status = DIRTY;
  257.     }
  258.   }
  259.  
  260. #endif
  261.  
  262. /*-----------------------------------------------------------------------------
  263. This function checks the value of c (which is always in the range from 0 to
  264. 255, inclusive). If it represents a character that cannot appear in a valid
  265. file name or extension, it bails out.
  266. -----------------------------------------------------------------------------*/
  267.  
  268. static void check_file_character(struct rdcf *f, unsigned c)
  269. {
  270.   static unsigned char table[32] =
  271.   {
  272.     0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC,
  273.     0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90,
  274.     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  275.     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  276.   };
  277.   if (table[c>>3] & 1<<(c&7)) error_exit(f, RDCF_INVALID_SPEC);
  278. }
  279.  
  280. /*-----------------------------------------------------------------------------
  281. This function edits a file or directory spec into the name-extension form used
  282. in the file directory entry.  It returns a pointer to the character
  283. following the last one of the spec.
  284. -----------------------------------------------------------------------------*/
  285.  
  286. static char *spec_to_name_extension(struct rdcf *f,
  287.   unsigned char *name_extension, unsigned char *spec)
  288. {
  289.   unsigned i = 0;
  290.   unsigned c;
  291.   while ((c=(*spec++))!=0 && c!='\\' && c!='.')
  292.   {
  293.     check_file_character(f,c);
  294.     if (i<NAME_SIZE) name_extension[i++] = toupper(c);
  295.   }
  296.   if (i==0) error_exit(f, RDCF_INVALID_SPEC);
  297.   while (i<NAME_SIZE) name_extension[i++] = ' ';
  298.   if (c=='.') while ((c=(*spec++))!=0 && c!='\\')
  299.   {
  300.     check_file_character(f,c);
  301.     if (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = toupper(c);
  302.   }
  303.   while (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = ' ';
  304.   return spec-1;
  305. }
  306.  
  307. /*-----------------------------------------------------------------------------
  308. This function edits the name-extension form used in a file entry to file spec.
  309. -----------------------------------------------------------------------------*/
  310.  
  311. static void name_extension_to_spec(unsigned char *spec,
  312.   unsigned char *name_extension)
  313. {
  314.   unsigned i;
  315.   unsigned char *s = spec;
  316.   for (i=0; i<NAME_SIZE && name_extension[i]!=' '; i++)
  317.     *s++ = name_extension[i];
  318.   if (name_extension[NAME_SIZE]!=' ')
  319.   {
  320.     *s++ = '.';
  321.     for (i=NAME_SIZE;
  322.       i<NAME_SIZE+EXTENSION_SIZE && name_extension[i]!=' '; i++)
  323.     {
  324.       *s++ = name_extension[i];
  325.     }
  326.   }
  327.   *s = 0;
  328. }
  329.  
  330. /*-----------------------------------------------------------------------------
  331. This function calculates the number of the first sector in the specified
  332. cluster.
  333. -----------------------------------------------------------------------------*/
  334.  
  335. static unsigned first_sector_in_cluster(struct rdcf *f, unsigned cluster)
  336. {
  337.   check_cluster(f, cluster);
  338.   return f->first_data_sector + (cluster-2) * f->sectors_per_cluster;
  339. }
  340.  
  341. /*-----------------------------------------------------------------------------
  342. This function finds the directory entry referred to by f->directory_cluster and
  343. f->directory_index, reads it into f->buffer, and returns a pointer to it.
  344. -----------------------------------------------------------------------------*/
  345.  
  346. static struct directory *find_directory(struct rdcf *f)
  347. {
  348.   read_buffer(f, (f->directory_cluster == 0 ? f->first_directory_sector :
  349.     first_sector_in_cluster(f, f->directory_cluster)) +
  350.     f->directory_index/ENTRIES_PER_SECTOR);
  351.   return
  352.     (struct directory *)(f->buffer) + f->directory_index%ENTRIES_PER_SECTOR;
  353. }
  354.  
  355. /*-----------------------------------------------------------------------------
  356. This function updates a directory entry.  If the "delete_entry" parameter is
  357. true (nonzero), it also marks the entry as deleted.
  358. -----------------------------------------------------------------------------*/
  359.  
  360. #ifndef RDCF_SYSTEM_READ_ONLY
  361.  
  362.   static void update_directory_entry(struct rdcf *f, int delete_entry)
  363.   {
  364.     unsigned i;
  365.     struct directory *d = find_directory(f);
  366.     if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.')
  367.       memcpy(d->name_extension, f->file.spec, NAME_SIZE+EXTENSION_SIZE);
  368.     else
  369.       spec_to_name_extension(f, d->name_extension, f->file.spec);
  370.     if (delete_entry) d->name_extension[0] = DELETED_FILE;
  371.     d->attribute = f->file.attribute;
  372.     d->date = (f->file.date_and_time.year-1980)<<9 |
  373.       f->file.date_and_time.month<<5 | f->file.date_and_time.day;
  374.     d->time = f->file.date_and_time.hour<<11 |
  375.       f->file.date_and_time.minute<<5 | f->file.date_and_time.second>>1;
  376.     d->first_cluster = f->file.first_cluster;
  377.     d->size = f->file.size;
  378.     memset(d->reserved, 0, sizeof(d->reserved));
  379.     #ifdef _BIG_ENDIAN
  380.       convert_short(d->date);
  381.       convert_short(d->time);
  382.       convert_short(d->first_cluster);
  383.       convert_long(d->size);
  384.     #endif
  385.     f->buffer_status = DIRTY;
  386.   }
  387.  
  388. #endif
  389.  
  390. /*-----------------------------------------------------------------------------
  391. This function reads directory information into an FCB.
  392. -----------------------------------------------------------------------------*/
  393.  
  394. static void read_directory_entry(struct rdcf *f)
  395. {
  396.   struct directory *d = find_directory(f);
  397.   if (d->attribute&RDCF_VOLUME)
  398.   {
  399.     memcpy(f->file.spec, d->name_extension, NAME_SIZE+EXTENSION_SIZE);
  400.     f->file.spec[NAME_SIZE+EXTENSION_SIZE] = 0;
  401.   }
  402.   else
  403.     name_extension_to_spec(f->file.spec, d->name_extension);
  404.   f->file.attribute = d->attribute;
  405.   {
  406.     unsigned short date = d->date;
  407.     #ifdef _BIG_ENDIAN
  408.       convert_short(date);
  409.     #endif
  410.     f->file.date_and_time.year = (date>>9)+1980;
  411.     f->file.date_and_time.month = date>>5&0xF;
  412.     f->file.date_and_time.day = date&0x1F;
  413.   }
  414.   {
  415.     unsigned short time = d->time;
  416.     #ifdef _BIG_ENDIAN
  417.       convert_short(time);
  418.     #endif
  419.     f->file.date_and_time.hour = time>>11;
  420.     f->file.date_and_time.minute = time>>5&0x3F;
  421.     f->file.date_and_time.second = time<<1&0x1F<<1;
  422.   }
  423.   f->file.first_cluster = d->first_cluster;
  424.   f->file.size = d->size;
  425.   #ifdef _BIG_ENDIAN
  426.     convert_short(f->file.first_cluster);
  427.     convert_long(f->file.size);
  428.   #endif
  429. }
  430.  
  431. /*-----------------------------------------------------------------------------
  432. If the parameter "name_extension" is not NULL, this function looks up the name
  433. and extension in the directory specified by f->directory_cluster (the entire
  434. root directory if f->directory_cluster == 0). If found, it puts the appropriate
  435. information into the file structure and returns a true (nonzero) result. If not
  436. found, it returns a false (zero) value and also puts the cluster and index of
  437. the first empty entry, if any, into f->directory_cluster and
  438. f->directory_index.  In any case, the name and extension are left in
  439. f->file.spec, and the first directory cluster is left in
  440. f->directory_first_cluster.
  441.  
  442. If the parameter name_extension is NULL, this function looks up the volume
  443. label in the same way (although in this case the calling function looks only in
  444. the root directory, of course).
  445.  
  446. If the file or volume label is not found and there is no empty entry, the
  447. special value NO_DIRECTORY_INDEX is left in f->directory_index.
  448. -----------------------------------------------------------------------------*/
  449.  
  450. static int find_file_in_directory_or_find_volume(struct rdcf *f,
  451.   unsigned char *name_extension)
  452. {
  453.   unsigned empty_cluster;
  454.   unsigned empty_index = NO_DIRECTORY_INDEX;
  455.   unsigned number_of_directory_entries = f->directory_cluster == 0 ?
  456.     (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR :
  457.     ENTRIES_PER_SECTOR*f->sectors_per_cluster;
  458.   f->directory_first_cluster = f->directory_cluster;
  459.   while (1)
  460.   {
  461.     for (f->directory_index = 0;
  462.       f->directory_index < number_of_directory_entries; f->directory_index++)
  463.     {
  464.       unsigned j;
  465.       struct directory *d = find_directory(f);
  466.       if ((d->name_extension[0] == DELETED_FILE ||
  467.         d->name_extension[0] == END_DIRECTORY) &&
  468.         empty_index == NO_DIRECTORY_INDEX)
  469.       {
  470.         empty_cluster = f->directory_cluster;
  471.         empty_index = f->directory_index;
  472.       }
  473.       if (d->name_extension[0] == END_DIRECTORY) break;
  474.       if (name_extension == NULL &&
  475.         (d->name_extension[0] != DELETED_FILE && d->attribute & RDCF_VOLUME) ||
  476.         name_extension != NULL &&
  477.         (memcmp(d->name_extension, name_extension, NAME_SIZE+EXTENSION_SIZE)
  478.         == 0 && (d->attribute&RDCF_VOLUME) == 0))
  479.       {
  480.         read_directory_entry(f);
  481.         return 1;
  482.       }
  483.     }
  484.     if (f->directory_index < number_of_directory_entries ||
  485.       f->directory_cluster == 0)
  486.     {
  487.       break;
  488.     }
  489.     {
  490.       unsigned x = FAT_entry(f, f->directory_cluster);
  491.       if (x >= f->last_cluster_mark) break;
  492.       f->directory_cluster = x;
  493.     }
  494.   }
  495.   f->directory_index = empty_index;
  496.   if (f->directory_index != NO_DIRECTORY_INDEX)
  497.     f->directory_cluster = empty_cluster;
  498.   if (name_extension != NULL)
  499.     name_extension_to_spec(f->file.spec, name_extension);
  500.   return 0;
  501. }
  502.  
  503. /*-----------------------------------------------------------------------------
  504. This function follows the directory path and finds the directory entry for the
  505. file (or directory) with the spec provided.  If found, it reads the directory
  506. information into the FCB and returns a true (nonzero) value.  Otherwise, it
  507. returns a false (zero) value and also puts the cluster and index of the first
  508. available directory entry, if any, into f->directory_cluster and
  509. f->directory_index.  If there is no available directory entry, it puts the
  510. special value NO_DIRECTORY_INDEX into f->directory_index. In any case, the
  511. number of the first cluster of the directory is left in
  512. f->directory_first_cluster and the name and extension are left in f->file.spec.
  513. -----------------------------------------------------------------------------*/
  514.  
  515. static int find_file(struct rdcf *f, char *spec)
  516. {
  517.   /* start with root directory */
  518.   f->directory_cluster = 0;
  519.   while (1)
  520.   {
  521.     int found;
  522.     unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
  523.     /* scan name and extension */
  524.     spec = spec_to_name_extension(f, name_extension, spec);
  525.     /* look it up in directory */
  526.     found = find_file_in_directory_or_find_volume(f, name_extension);
  527.     /* if this is the end of the file specification, return */
  528.     if (*spec == 0) return found;
  529.     /* otherwise, the name and extension were a subdirectory in the path */
  530.     if (!found || (f->file.attribute&RDCF_DIRECTORY) == 0)
  531.       error_exit(f, RDCF_INVALID_DIRECTORY);
  532.     f->directory_cluster = f->file.first_cluster;
  533.     /* skip over the \ after the subdirectory */
  534.     spec++;
  535.   }
  536. }
  537.  
  538. /*-----------------------------------------------------------------------------
  539. This function reads the file system information from the first sector.
  540. -----------------------------------------------------------------------------*/
  541.  
  542. static void read_file_system_information(struct rdcf *f)
  543. {
  544.   unsigned total_sectors;
  545.   unsigned i;
  546.   unsigned char *buffer = f->buffer;
  547.   read_buffer(f, 0);
  548.   /* make a perfunctory sanity check to prevent later division by zero */
  549.   if ((total_sectors = TOTAL_SECTORS) == 0 ||
  550.     #ifdef RDCF_SECTOR_SIZE
  551.       BYTES_PER_SECTOR != SECTOR_SIZE ||
  552.     #else
  553.       (SECTOR_SIZE = BYTES_PER_SECTOR) == 0 ||
  554.     #endif
  555.     (f->first_FAT_sector = RESERVED_SECTORS) == 0 ||
  556.     (f->sectors_per_FAT = SECTORS_PER_FAT) == 0 ||
  557.     (f->sectors_per_cluster = SECTORS_PER_CLUSTER) == 0)
  558.   {
  559.     error_exit(f, RDCF_DISK_FORMAT_ERROR);
  560.   }
  561.   f->first_directory_sector =
  562.     f->first_FAT_sector + f->sectors_per_FAT * NUMBER_OF_FATS;
  563.   f->first_data_sector = f->first_directory_sector +
  564.     ROOT_ENTRIES/ENTRIES_PER_SECTOR;
  565.   f->maximum_cluster_number =
  566.     (total_sectors-f->first_data_sector) / SECTORS_PER_CLUSTER + 1;
  567.   f->last_cluster_mark =
  568.     f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT ?
  569.     LAST_CLUSTER_12_BIT : LAST_CLUSTER_16_BIT;
  570. }
  571.  
  572. /*-----------------------------------------------------------------------------
  573. This function gets the drive specifications and returns a pointer to the
  574. character following the drive specifications.
  575. -----------------------------------------------------------------------------*/
  576.  
  577. #ifdef RDCF_MULTIPLE_DRIVE
  578.  
  579.   static char *get_drive(struct rdcf *f, char *spec)
  580.   {
  581.     if (spec[0] != 0 && spec[1] == ':')
  582.     {
  583.       if (isalpha(spec[0])) f->drive = toupper(spec[0])-'A';
  584.       else error_exit(f, RDCF_INVALID_SPEC);
  585.       return spec + 2;
  586.     }
  587.     error_exit(f, RDCF_INVALID_SPEC);
  588.   }
  589.  
  590. #endif
  591.  
  592. /*-----------------------------------------------------------------------------
  593. This function scans the file spec and sets up the file control block for
  594. further file operations.  It returns a pointer to the character following the
  595. drive specifications.
  596. -----------------------------------------------------------------------------*/
  597.  
  598. static char *initialize_fcb(struct rdcf *f, char *spec)
  599. {
  600.   #ifdef RDCF_MULTIPLE_DRIVE
  601.     spec = get_drive(f, spec);
  602.   #endif
  603.   f->buffer_status = EMPTY;
  604.   read_file_system_information(f);
  605.   return spec;
  606. }
  607.  
  608. /*-----------------------------------------------------------------------------
  609. This function checks write access and generates an error if write access is
  610. denied.
  611. -----------------------------------------------------------------------------*/
  612.  
  613. #ifndef RDCF_SYSTEM_READ_ONLY
  614.  
  615.   static void check_write_access(struct rdcf *f)
  616.   {
  617.     if (f->file.attribute & (RDCF_READ_ONLY+RDCF_HIDDEN+RDCF_SYSTEM))
  618.       error_exit(f, RDCF_ACCESS_DENIED);
  619.   }
  620.  
  621. #endif
  622.  
  623. /*-----------------------------------------------------------------------------
  624. This function releases all the FAT entries of a file.
  625. -----------------------------------------------------------------------------*/
  626.  
  627. #ifndef RDCF_SYSTEM_READ_ONLY
  628.  
  629.   static void release_FAT_entries(struct rdcf *f)
  630.   {
  631.     unsigned j;
  632.     j = f->file.first_cluster;
  633.     if (j != EMPTY_CLUSTER)
  634.     {
  635.       while (j < f->last_cluster_mark)
  636.       {
  637.         unsigned k = FAT_entry(f,j);
  638.         set_FAT_entry(f,j,EMPTY_CLUSTER);
  639.         j = k;
  640.       }
  641.     }
  642.     f->mode |= WRITTEN;
  643.   }
  644.  
  645. #endif
  646.  
  647. /*-----------------------------------------------------------------------------
  648. This function finds a new cluster, if possible, and adds it to a chain ending
  649. with the specified cluster.  It returns the new cluster number, or
  650. EMPTY_CLUSTER if there are no free clusters.
  651. -----------------------------------------------------------------------------*/
  652.  
  653. static unsigned add_new_cluster(struct rdcf *f, unsigned cluster,
  654.   unsigned first_possibly_empty_cluster)
  655. {
  656.   unsigned new_cluster;
  657.   for (new_cluster = first_possibly_empty_cluster;
  658.     new_cluster <= f->maximum_cluster_number; new_cluster++)
  659.   {
  660.     if (FAT_entry(f, new_cluster)==EMPTY_CLUSTER) break;
  661.   }
  662.   if (new_cluster > f->maximum_cluster_number) return EMPTY_CLUSTER;
  663.   if (cluster != EMPTY_CLUSTER) set_FAT_entry(f, cluster, new_cluster);
  664.   set_FAT_entry(f, new_cluster, f->last_cluster_mark);
  665.   return new_cluster;
  666. }
  667.  
  668. /*-----------------------------------------------------------------------------
  669. This function writes zeros into a cluster.
  670. -----------------------------------------------------------------------------*/
  671.  
  672. #ifndef RDCF_SYSTEM_READ_ONLY
  673.  
  674.   static void clear_cluster(struct rdcf *f, unsigned cluster)
  675.   {
  676.     unsigned count = f->sectors_per_cluster;
  677.     unsigned sector = first_sector_in_cluster(f, cluster);
  678.     flush_buffer(f);
  679.     f->buffer_status = EMPTY;
  680.     memset(f->buffer, 0, SECTOR_SIZE);
  681.     do write_sector(f, sector++, f->buffer); while (--count != 0);
  682.   }
  683.  
  684. #endif
  685.  
  686. /*-----------------------------------------------------------------------------
  687. This function adds another cluster to the directory if necessary to accommodate
  688. a new file or subdirectory.
  689. -----------------------------------------------------------------------------*/
  690.  
  691. #ifndef RDCF_SYSTEM_READ_ONLY
  692.  
  693.   static void lengthen_directory_if_necessary(struct rdcf *f)
  694.   {
  695.     if (f->directory_index == NO_DIRECTORY_INDEX)
  696.     {
  697.       if (f->directory_cluster == 0)
  698.         error_exit(f, RDCF_DIRECTORY_FULL);
  699.       f->directory_cluster = add_new_cluster(f, f->directory_cluster, 2);
  700.       if (f->directory_cluster == 0)
  701.         error_exit(f, RDCF_DIRECTORY_FULL);
  702.       f->directory_index = 0;
  703.       clear_cluster(f, f->directory_cluster);
  704.     }
  705.   }
  706.  
  707. #endif
  708.  
  709. /*-----------------------------------------------------------------------------
  710. When f represents a subdirectory, this function updates the . and .. entries at
  711. the beginning of its first cluster.
  712. -----------------------------------------------------------------------------*/
  713.  
  714. #ifndef RDCF_SYSTEM_READ_ONLY
  715.  
  716.   static void update_dot_and_dot_dot(struct rdcf *f)
  717.   {
  718.     f->directory_cluster = f->file.first_cluster;
  719.     f->directory_index = 0;
  720.     memset(f->file.spec, ' ', NAME_SIZE+EXTENSION_SIZE);
  721.     f->file.spec[0] = '.';
  722.     update_directory_entry(f, 0);
  723.     f->file.first_cluster = f->directory_first_cluster;
  724.     f->directory_index = 1;
  725.     f->file.spec[1] = '.';
  726.     update_directory_entry(f, 0);
  727.   }
  728.  
  729. #endif
  730.  
  731. /*-----------------------------------------------------------------------------
  732. This function contains the common code from rdcf_get_file_information() and
  733. rdcf_set_file_information().  It finds the directory entry specified by the
  734. spec and index, and puts its cluster and index into f->directory_cluster and
  735. f->directory_index.
  736. -----------------------------------------------------------------------------*/
  737.  
  738. static void find_entry(struct rdcf *f, char *spec, unsigned index)
  739. {
  740.   spec = initialize_fcb(f, spec);
  741.   if (*spec == 0)
  742.   {
  743.     if (index >=
  744.       (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR)
  745.     {
  746.       error_exit(f, RDCF_DIRECTORY_FULL);
  747.     }
  748.     f->directory_first_cluster = f->directory_cluster = 0;
  749.   }
  750.   else
  751.   {
  752.     if (!find_file(f, spec) || (f->file.attribute&RDCF_DIRECTORY) == 0)
  753.       error_exit(f, RDCF_INVALID_DIRECTORY);
  754.     f->directory_first_cluster = f->directory_cluster = f->file.first_cluster;
  755.     while (index >= ENTRIES_PER_SECTOR*f->sectors_per_cluster)
  756.     {
  757.       f->directory_cluster = FAT_entry(f, f->directory_cluster);
  758.       if (f->directory_cluster >= f->last_cluster_mark)
  759.         error_exit(f,RDCF_DIRECTORY_FULL);
  760.       index -= ENTRIES_PER_SECTOR*f->sectors_per_cluster;
  761.     }
  762.   }
  763.   f->directory_index = index;
  764. }
  765.  
  766. /*-----------------------------------------------------------------------------
  767. This function is called by rdcf_sort_directory() to determine the class of a
  768. directory entry.
  769. -----------------------------------------------------------------------------*/
  770.  
  771. static int entry_class(struct directory *d)
  772. {
  773.   enum {VOLUME_ENTRY, DIRECTORY_ENTRY, FILE_ENTRY, ERASED_ENTRY};
  774.   return d->name_extension[0] == DELETED_FILE ? ERASED_ENTRY :
  775.     d->attribute & RDCF_DIRECTORY ? DIRECTORY_ENTRY :
  776.     d->attribute & RDCF_VOLUME ? VOLUME_ENTRY : FILE_ENTRY;
  777. }
  778.  
  779. /*-----------------------------------------------------------------------------
  780. This function is called by rdcf_sort_directory() to compare two four-byte
  781. fields.
  782. -----------------------------------------------------------------------------*/
  783.  
  784. static int compare_four_bytes(unsigned char *p, unsigned char *q)
  785. {
  786.   int i, n;
  787.   for (i=3; i>=0 && (n=p[i]-q[i]) == 0; i--);
  788.   return n;
  789. }
  790.    
  791. /*-----------------------------------------------------------------------------
  792. The following functions are publicly defined.  They do not call each other and
  793. any of them may be removed without making changes to those that remain.  The
  794. functions are defined in alphabetical order.
  795. -----------------------------------------------------------------------------*/
  796.  
  797. #ifndef RDCF_SYSTEM_READ_ONLY
  798.  
  799.   #define CHANGEABLE_ATTRIBUTES \
  800.     (RDCF_ARCHIVE+RDCF_HIDDEN+RDCF_READ_ONLY+RDCF_SYSTEM)
  801.  
  802.   int rdcf_attribute(struct rdcf *f, char *spec, unsigned attribute)
  803.   {
  804.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  805.     if (!find_file(f, initialize_fcb(f, spec)) ||
  806.       f->file.attribute & RDCF_DIRECTORY)
  807.     {
  808.       error_exit(f, RDCF_FILE_NOT_FOUND);
  809.     }
  810.     f->file.attribute = f->file.attribute & ~CHANGEABLE_ATTRIBUTES |
  811.       attribute & CHANGEABLE_ATTRIBUTES;
  812.     update_directory_entry(f, 0);
  813.     flush_buffer(f);
  814.     return 0;
  815.   }
  816.  
  817. #endif
  818.  
  819. #ifndef RDCF_SYSTEM_READ_ONLY
  820.  
  821.   int rdcf_close(struct rdcf *f)
  822.   {
  823.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  824.     if (f->mode&WRITTEN)
  825.     {
  826.       f->buffer_status = EMPTY;
  827.       f->file.attribute |= RDCF_ARCHIVE;
  828.       rdcf_get_date_and_time(&f->file.date_and_time);
  829.       update_directory_entry(f, 0);
  830.       flush_buffer(f);
  831.     }
  832.     return 0;
  833.   }
  834.  
  835. #endif
  836.  
  837. #ifndef RDCF_SYSTEM_READ_ONLY
  838.  
  839.   int rdcf_date_and_time(struct rdcf *f, char *spec,
  840.     struct rdcf_date_and_time *p)
  841.   {
  842.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  843.     spec = initialize_fcb(f, spec);
  844.     if (*spec == 0)
  845.     {
  846.       f->directory_cluster = 0;
  847.       if (!find_file_in_directory_or_find_volume(f, NULL))
  848.         error_exit(f, RDCF_FILE_NOT_FOUND);
  849.     }
  850.     else
  851.     {
  852.       if (!find_file(f, spec)) error_exit(f, RDCF_FILE_NOT_FOUND);
  853.     }
  854.     f->file.date_and_time = *p;
  855.     if ((f->file.attribute & RDCF_DIRECTORY+RDCF_VOLUME) == 0)
  856.     {
  857.       check_write_access(f);
  858.       f->file.attribute |= RDCF_ARCHIVE;
  859.     }
  860.     update_directory_entry(f, 0);
  861.     if (f->file.attribute & RDCF_DIRECTORY) update_dot_and_dot_dot(f);
  862.     flush_buffer(f);
  863.     return 0;
  864.   }
  865.  
  866. #endif
  867.  
  868. #ifndef RDCF_SYSTEM_READ_ONLY
  869.  
  870.   int rdcf_delete(struct rdcf *f, char *spec)
  871.   {
  872.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  873.     spec = initialize_fcb(f, spec);
  874.     if (*spec == 0) /* delete volume label */
  875.     {
  876.       if (!find_file_in_directory_or_find_volume(f, NULL))
  877.         error_exit(f, RDCF_FILE_NOT_FOUND);
  878.     }
  879.     else if (!find_file(f, spec))
  880.       error_exit(f, RDCF_FILE_NOT_FOUND);
  881.     /* check to see that a directory is empty before deleting it */
  882.     else if (f->file.attribute & RDCF_DIRECTORY)
  883.     {
  884.       unsigned cluster = f->file.first_cluster;
  885.       while (cluster != EMPTY_CLUSTER && cluster < f->last_cluster_mark)
  886.       {
  887.         unsigned sector = first_sector_in_cluster(f, cluster);
  888.         unsigned sector_count = f->sectors_per_cluster;
  889.         unsigned next_cluster = FAT_entry(f, cluster);
  890.         do
  891.         {
  892.           unsigned entry_count = ENTRIES_PER_SECTOR;
  893.           unsigned char *p = f->buffer;
  894.           read_buffer(f, sector);
  895.           do
  896.           {
  897.             unsigned c = *p;
  898.             if (c == END_DIRECTORY) break;
  899.             if (c != DELETED_FILE && c != '.')
  900.               error_exit(f, RDCF_ACCESS_DENIED);
  901.             p += sizeof(struct directory);
  902.           } while (--entry_count != 0);
  903.           if (entry_count != 0) break;
  904.         } while (--sector_count != 0);
  905.         cluster = next_cluster;
  906.       }
  907.     }
  908.     else
  909.       check_write_access(f);
  910.     release_FAT_entries(f);
  911.     update_directory_entry(f, 1);
  912.     flush_buffer(f);
  913.     return 0;
  914.   }
  915.  
  916. #endif
  917.  
  918. #ifndef RDCF_SYSTEM_READ_ONLY
  919.  
  920.   int rdcf_directory(struct rdcf *f, char *spec)
  921.   {
  922.     /* unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE]; ??? */
  923.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  924.     if (find_file(f, initialize_fcb(f, spec)))
  925.       error_exit(f,RDCF_DIRECTORY_CONFLICT);
  926.     /* spec_to_name_extension(f, name_extension, f->file.spec); ??? */
  927.     /* name_extension_to_spec(f->file.spec, name_extension); ??? */
  928.     /* determine whether there is enough free space for directory */
  929.     {
  930.       unsigned cluster = 2;
  931.       unsigned required_clusters =
  932.         f->directory_index == NO_DIRECTORY_INDEX ? 2 : 1;
  933.       for (cluster = 2; required_clusters != 0; cluster++)
  934.       {
  935.         if (cluster > f->maximum_cluster_number)
  936.           error_exit(f,RDCF_DIRECTORY_FULL);
  937.         if (FAT_entry(f, cluster)==EMPTY_CLUSTER) required_clusters--;
  938.       }
  939.     }
  940.     lengthen_directory_if_necessary(f);
  941.     f->file.attribute = RDCF_DIRECTORY;
  942.     f->file.first_cluster = add_new_cluster(f, EMPTY_CLUSTER, 2);
  943.     clear_cluster(f, f->file.first_cluster);
  944.     f->file.size = 0L;
  945.     rdcf_get_date_and_time(&f->file.date_and_time);
  946.     update_directory_entry(f, 0);
  947.     update_dot_and_dot_dot(f);
  948.     flush_buffer(f);
  949.     return 0;
  950.   }
  951.  
  952. #endif
  953.  
  954. #ifndef RDCF_SYSTEM_READ_ONLY
  955.  
  956.   struct rdcf_format rdcf_format_360 =
  957.   {
  958.     0,2,2,1,0,2,112,0,208,2,253,2,0,9,0,2,0,0,0
  959.   };
  960.  
  961.   struct rdcf_format rdcf_format_720 =
  962.   {
  963.     0,2,2,1,0,2,112,0,160,5,249,3,0,9,0,2,0,0,0
  964.   };
  965.  
  966.   struct rdcf_format rdcf_format_1200 =
  967.   {
  968.     0,2,1,1,0,2,224,0,96,9,249,7,0,15,0,2,0,0,0
  969.   };
  970.  
  971.   struct rdcf_format rdcf_format_1440 =
  972.   {
  973.     0,2,1,1,0,2,224,0,64,11,240,9,0,18,0,2,0,0,0
  974.   };
  975.  
  976.   int rdcf_format(struct rdcf *f,
  977.     #ifdef RDCF_MULTIPLE_DRIVE
  978.       char *spec,
  979.     #endif
  980.     struct rdcf_format *format)
  981.   {
  982.     static char bootstrap_header[] =
  983.     {
  984.       235,254,144,'R','D','C','F','2','0',' ',' '
  985.     };
  986.     unsigned char *buffer = f->buffer;
  987.     unsigned char media_descriptor;
  988.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  989.     #ifdef RDCF_MULTIPLE_DRIVE
  990.       if (*get_drive(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
  991.     #endif
  992.     memset(buffer, 0, SECTOR_SIZE);
  993.     memcpy(buffer, bootstrap_header, sizeof(bootstrap_header));
  994.     memcpy(buffer + sizeof(bootstrap_header), format,
  995.       sizeof(struct rdcf_format));
  996.     buffer[SECTOR_SIZE-2] = 0x55;
  997.     buffer[SECTOR_SIZE-1] = 0xAA;
  998.     write_sector(f, 0, buffer);
  999.     f->buffer_status = CLEAN;
  1000.     f->sector_in_buffer = 0;
  1001.     read_file_system_information(f);
  1002.     media_descriptor = MEDIA_DESCRIPTOR;
  1003.     memset(buffer, 0, SECTOR_SIZE);
  1004.     {
  1005.       unsigned sector = f->first_FAT_sector;
  1006.       while (sector < f->first_directory_sector)
  1007.       {
  1008.         unsigned count = f->sectors_per_FAT;
  1009.         buffer[0] = media_descriptor;
  1010.         buffer[1] = buffer[2] = 0xFF;
  1011.         write_sector(f, sector++, buffer);
  1012.         buffer[0] = buffer[1] = buffer[2] = 0;
  1013.         while (--count != 0)
  1014.           write_sector(f, sector++, buffer);
  1015.       }
  1016.       buffer[0] = buffer[1] = buffer[2] = 0;
  1017.       while (sector < f->first_data_sector)
  1018.          write_sector(f, sector++, buffer);
  1019.     }
  1020.     return 0;
  1021.   }
  1022.  
  1023. #endif
  1024.  
  1025. long int rdcf_free_space(struct rdcf *f
  1026.   #ifdef RDCF_MULTIPLE_DRIVE
  1027.     , char *spec
  1028.   #endif
  1029.   )
  1030. {
  1031.   unsigned cluster;
  1032.   unsigned number_of_empty_clusters = 0;
  1033.   if ((f->result=setjmp(f->error)) != 0) return (long)(f->result);
  1034.   #ifndef RDCF_MULTIPLE_DRIVE
  1035.     initialize_fcb(f, NULL);
  1036.   #else
  1037.     if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
  1038.   #endif
  1039.   for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++)
  1040.   {
  1041.     if (FAT_entry(f, cluster) == EMPTY_CLUSTER) number_of_empty_clusters++;
  1042.   }
  1043.   f->file.size = (unsigned long) number_of_empty_clusters *
  1044.     (f->sectors_per_cluster * SECTOR_SIZE);
  1045.   return (long)(f->file.size);
  1046. }
  1047.  
  1048. int rdcf_get_file_information(struct rdcf *f, char *spec, unsigned index)
  1049. {
  1050.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1051.   find_entry(f, spec, index);
  1052.   read_directory_entry(f);
  1053.   return f->result = f->file.spec[0] == DELETED_FILE ? RDCF_FILE_NOT_FOUND :
  1054.     f->file.spec[0] == 0 ? RDCF_DIRECTORY_FULL : 0;
  1055. }
  1056.  
  1057. int rdcf_get_volume(struct rdcf *f
  1058.   #ifdef RDCF_MULTIPLE_DRIVE
  1059.     , char *spec
  1060.   #endif
  1061.   )
  1062. {
  1063.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1064.   #ifndef RDCF_MULTIPLE_DRIVE
  1065.     initialize_fcb(f, NULL);
  1066.   #else
  1067.     if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
  1068.   #endif
  1069.   f->directory_cluster = 0;
  1070.   if (!find_file_in_directory_or_find_volume(f, NULL))
  1071.     error_exit(f, RDCF_FILE_NOT_FOUND);
  1072.   return 0;
  1073. }
  1074.  
  1075. int rdcf_match(char *specs, char *pattern)
  1076. {
  1077.   unsigned size = NAME_SIZE;
  1078.   while (1)
  1079.   {
  1080.     unsigned count = size;
  1081.     do
  1082.     {
  1083.       int c1, c2;
  1084.       if (*specs == '.' || *specs == 0) c1 = ' ';
  1085.       else c1 = *specs++;
  1086.       if (*pattern == '*') c2 = '?';
  1087.       else if (*pattern == '.' || *pattern == 0) c2 = ' ';
  1088.       else c2 = *pattern++;
  1089.       if (c2 != '?' && toupper(c1) != toupper(c2)) return 0;
  1090.     } while (--count != 0);
  1091.     while (*specs != '.' && *specs != 0) specs++;
  1092.     if (*specs == '.') ++specs;
  1093.     if (*pattern == '*') while (*++pattern != '.' && *pattern != 0);
  1094.     if (*pattern == '.') ++pattern;
  1095.     if (size == NAME_SIZE) size = EXTENSION_SIZE;
  1096.     else return 1;
  1097.   }
  1098. }
  1099.  
  1100. #ifndef RDCF_SYSTEM_READ_ONLY
  1101.  
  1102.   int rdcf_move(struct rdcf *f, char *old_spec, char *new_spec)
  1103.   {
  1104.     unsigned char spec[13];
  1105.     unsigned short first_cluster;
  1106.     unsigned char attribute;
  1107.     struct rdcf_date_and_time date_and_time;
  1108.     unsigned long size;
  1109.     unsigned short directory_cluster;
  1110.     unsigned short directory_index;
  1111.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1112.     if (!find_file(f, initialize_fcb(f, old_spec)))
  1113.       error_exit(f, RDCF_FILE_NOT_FOUND);
  1114.     /* save all needed information about old spec */
  1115.     memcpy(spec, f->file.spec, sizeof(spec));
  1116.     attribute = f->file.attribute;
  1117.     date_and_time = f->file.date_and_time;
  1118.     size = f->file.size;
  1119.     first_cluster = f->file.first_cluster;
  1120.     directory_cluster = f->directory_cluster;
  1121.     directory_index = f->directory_index;
  1122.     if (find_file(f, new_spec)) error_exit(f, RDCF_RENAMING_ERROR);
  1123.     f->file.first_cluster = first_cluster;
  1124.     f->file.attribute = attribute;
  1125.     f->file.date_and_time = date_and_time;
  1126.     f->file.size = size;
  1127.     lengthen_directory_if_necessary(f);
  1128.     update_directory_entry(f, 0);
  1129.     if (f->file.attribute & RDCF_DIRECTORY)
  1130.       update_dot_and_dot_dot(f);
  1131.     memcpy(f->file.spec, spec, sizeof(spec));
  1132.     f->directory_cluster = directory_cluster;
  1133.     f->directory_index = directory_index;
  1134.     update_directory_entry(f, 1);
  1135.     flush_buffer(f);
  1136.     return 0;
  1137.   }
  1138.  
  1139.  
  1140. #endif
  1141.  
  1142. int rdcf_next_file_information(struct rdcf *f)
  1143. {
  1144.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1145.   f->directory_index++;
  1146.   if (f->directory_cluster == 0)
  1147.   {
  1148.     if (f->directory_index >=
  1149.       (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR)
  1150.     {
  1151.       error_exit(f, RDCF_DIRECTORY_FULL);
  1152.     }
  1153.   }
  1154.   else
  1155.   {
  1156.     if (f->directory_index >= ENTRIES_PER_SECTOR*f->sectors_per_cluster)
  1157.     {
  1158.       f->directory_cluster = FAT_entry(f, f->directory_cluster);
  1159.       if (f->directory_cluster >= f->last_cluster_mark)
  1160.         error_exit(f,RDCF_DIRECTORY_FULL);
  1161.       f->directory_index = 0;
  1162.     }
  1163.   }
  1164.   read_directory_entry(f);
  1165.   return f->result = f->file.spec[0] == DELETED_FILE ? RDCF_FILE_NOT_FOUND :
  1166.     f->file.spec[0] == 0 ? RDCF_DIRECTORY_FULL : 0;
  1167. }
  1168.  
  1169.  
  1170. int rdcf_open(struct rdcf *f, char *spec
  1171.   #ifndef RDCF_SYSTEM_READ_ONLY
  1172.     , unsigned mode
  1173.   #endif
  1174.   )
  1175. {
  1176.   int found;
  1177.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1178.   #ifndef RDCF_SYSTEM_READ_ONLY
  1179.     f->mode = mode;
  1180.   #endif
  1181.   found = find_file(f, initialize_fcb(f, spec));
  1182.   if (found && f->file.attribute&RDCF_DIRECTORY)
  1183.     error_exit(f,RDCF_DIRECTORY_CONFLICT);
  1184.   #ifdef RDCF_SYSTEM_READ_ONLY
  1185.     if (!found) error_exit(f, RDCF_FILE_NOT_FOUND);
  1186.   #else
  1187.     switch (mode)
  1188.     {
  1189.       case RDCF_READ_WRITE:
  1190.         check_write_access(f);
  1191.         /* fall through to case RDCF_READ */
  1192.       case RDCF_READ:
  1193.         if (!found)
  1194.           error_exit(f,RDCF_FILE_NOT_FOUND);
  1195.         break;
  1196.       case RDCF_CREATE:
  1197.         if (found)
  1198.         {
  1199.           check_write_access(f);
  1200.           release_FAT_entries(f);
  1201.         }
  1202.         else
  1203.           lengthen_directory_if_necessary(f);
  1204.         f->file.attribute = RDCF_ARCHIVE;
  1205.         f->file.first_cluster = EMPTY_CLUSTER;
  1206.         f->file.size = 0L;
  1207.         rdcf_get_date_and_time(&f->file.date_and_time);
  1208.         f->mode |= WRITTEN;
  1209.         update_directory_entry(f, 0);
  1210.         flush_buffer(f);
  1211.         break;
  1212.       default:
  1213.         error_exit(f,RDCF_MODE_ERROR);
  1214.     }
  1215.   #endif
  1216.   f->position = 0;
  1217.   f->last_cluster = EMPTY_CLUSTER;
  1218.   f->cluster = f->file.first_cluster;
  1219.   return 0;
  1220. }
  1221.  
  1222. int rdcf_read(struct rdcf *f, void *buf, int count)
  1223. {
  1224.   unsigned long size = f->file.size;
  1225.   unsigned unread_bytes = count;
  1226.   unsigned long position = f->position;
  1227.   char *buffer = buf;
  1228.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1229.   #ifndef RDCF_SYSTEM_READ_ONLY
  1230.     if (f->mode != RDCF_READ) error_exit(f,RDCF_MODE_ERROR);
  1231.   #endif
  1232.   f->buffer_status = EMPTY;
  1233.   while (unread_bytes>0)
  1234.   {
  1235.     unsigned n = unread_bytes;
  1236.     unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE;
  1237.     unsigned sector;
  1238.     if (size<position+n) n = size-position;
  1239.     if (n==0) break;
  1240.     sector = first_sector_in_cluster(f, f->cluster) +
  1241.       (position/SECTOR_SIZE)%f->sectors_per_cluster;
  1242.     if (n>rest_of_sector) n = rest_of_sector;
  1243.     if (position%SECTOR_SIZE==0 && n==SECTOR_SIZE)
  1244.       read_sector(f, sector, buffer);
  1245.     else /* read a partial sector */
  1246.     {
  1247.       read_buffer(f, sector);
  1248.       memcpy(buffer, f->buffer+position%SECTOR_SIZE, n);
  1249.     }
  1250.     buffer += n;
  1251.     position += n;
  1252.     unread_bytes -= n;
  1253.     if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0 && position<size)
  1254.     {
  1255.       unsigned next_cluster = FAT_entry(f, f->cluster);
  1256.       if (next_cluster >= f->last_cluster_mark) f->last_cluster = f->cluster;
  1257.       f->cluster = next_cluster;
  1258.     }
  1259.   }
  1260.   f->position = position;
  1261.   return f->result = count - unread_bytes;
  1262. }
  1263.  
  1264. #ifndef RDCF_SYSTEM_READ_ONLY
  1265.  
  1266.   int rdcf_recover(struct rdcf *f, char *spec)
  1267.   {
  1268.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1269.     if (find_file(f, initialize_fcb(f, spec)))
  1270.       error_exit(f,RDCF_ACCESS_DENIED);
  1271.     if (f->directory_index == NO_DIRECTORY_INDEX)
  1272.       error_exit(f,RDCF_DIRECTORY_FULL);
  1273.     f->file.attribute = RDCF_ARCHIVE;
  1274.     rdcf_get_date_and_time(&f->file.date_and_time);
  1275.     f->file.first_cluster = add_new_cluster(f, EMPTY_CLUSTER, 2);
  1276.     f->file.size = 0L;
  1277.     {
  1278.       unsigned cluster;
  1279.       for (cluster = f->file.first_cluster; cluster != EMPTY_CLUSTER;
  1280.         cluster = add_new_cluster(f, cluster, cluster))
  1281.       {
  1282.         f->file.size += f->sectors_per_cluster * SECTOR_SIZE;
  1283.       }
  1284.     }
  1285.     update_directory_entry(f, 0);
  1286.     flush_buffer(f);
  1287.     return 0;
  1288.   }
  1289.  
  1290. #endif
  1291.  
  1292. #ifndef RDCF_SYSTEM_READ_ONLY
  1293.  
  1294.   int rdcf_rename(struct rdcf *f, char *old_spec, char *new_spec)
  1295.   {
  1296.     unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
  1297.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1298.     old_spec = initialize_fcb(f, old_spec);
  1299.     if (!find_file(f, old_spec))
  1300.       error_exit(f,RDCF_FILE_NOT_FOUND);
  1301.     if (f->file.attribute & (RDCF_HIDDEN+RDCF_SYSTEM))
  1302.       error_exit(f, RDCF_ACCESS_DENIED);
  1303.     spec_to_name_extension(f, name_extension, new_spec);
  1304.     f->directory_cluster = f->directory_first_cluster;
  1305.     if (find_file_in_directory_or_find_volume(f, name_extension))
  1306.       error_exit(f,RDCF_RENAMING_ERROR);
  1307.     find_file(f, old_spec);
  1308.     name_extension_to_spec(f->file.spec, name_extension);
  1309.     update_directory_entry(f, 0);
  1310.     flush_buffer(f);
  1311.     return 0;
  1312.   }
  1313.  
  1314. #endif
  1315.  
  1316. int rdcf_seek(struct rdcf *f, unsigned long offset)
  1317. {
  1318.   unsigned i, cluster;
  1319.   if ((f->result=setjmp(f->error)) != 0) return f->result;
  1320.   if (offset>f->file.size) error_exit(f,RDCF_SEEK_OUT_OF_RANGE);
  1321.   f->buffer_status = EMPTY;
  1322.   cluster = f->file.first_cluster;
  1323.   for (i=offset/(f->sectors_per_cluster*SECTOR_SIZE); i>0; i--)
  1324.   {
  1325.     unsigned new_cluster = FAT_entry(f, cluster);
  1326.     if (new_cluster >= f->last_cluster_mark) f->last_cluster = cluster;
  1327.     cluster = new_cluster;
  1328.   }
  1329.   f->cluster = cluster;
  1330.   f->position = offset;
  1331.   return 0;
  1332. }
  1333.  
  1334. #ifndef RDCF_SYSTEM_READ_ONLY
  1335.  
  1336.   int rdcf_set_file_information(struct rdcf *f, char *spec, unsigned index,
  1337.     struct rdcf_file_information *file_information)
  1338.   {
  1339.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1340.     find_entry(f, spec, index);
  1341.     f->file = *file_information;
  1342.     if (f->file.spec[0] == 0)
  1343.       memset(find_directory(f), 0, sizeof(struct directory));
  1344.     else if (f->file.spec[0] == DELETED_FILE)
  1345.     {
  1346.       f->file.spec[0] = 'X';
  1347.       update_directory_entry(f, 1);
  1348.     }
  1349.     else
  1350.       update_directory_entry(f, 0);
  1351.     flush_buffer(f);
  1352.     return 0;
  1353.   }
  1354.  
  1355. #endif
  1356.  
  1357. #ifndef RDCF_SYSTEM_READ_ONLY
  1358.  
  1359.   int rdcf_set_volume(struct rdcf *f, char *spec)
  1360.   {
  1361.     unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
  1362.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1363.     spec = initialize_fcb(f, spec);
  1364.     {
  1365.       unsigned i = 0;
  1366.       for (i = 0; i < sizeof(name_extension); i++)
  1367.         name_extension[i] = *spec != 0 ? *spec++ : ' ';
  1368.     }
  1369.     if (name_extension[0] == DELETED_FILE)
  1370.       error_exit(f, RDCF_INVALID_SPEC);
  1371.     find_file_in_directory_or_find_volume(f, NULL);
  1372.     if (f->directory_index == NO_DIRECTORY_INDEX)
  1373.       error_exit(f,RDCF_DIRECTORY_FULL);
  1374.     memcpy(f->file.spec, name_extension, sizeof(name_extension));
  1375.     rdcf_get_date_and_time(&f->file.date_and_time);
  1376.     f->file.attribute = RDCF_VOLUME;
  1377.     f->file.size = 0;
  1378.     f->file.first_cluster = 0;
  1379.     update_directory_entry(f, 0);
  1380.     flush_buffer(f);
  1381.     return 0;
  1382.   }
  1383.  
  1384. #endif
  1385.  
  1386. #ifndef RDCF_SYSTEM_READ_ONLY
  1387.  
  1388.   int rdcf_sort_directory(struct rdcf *f, char *spec, int mode)
  1389.   {
  1390.     int sorting = 1;
  1391.     unsigned first_index;
  1392.     unsigned number_of_directory_entries;
  1393.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1394.     find_entry(f, spec, 0);
  1395.     read_directory_entry(f);
  1396.     if (f->file.spec[0] == 0) return 0; /* empty directory */
  1397.     /* do not sort subdirectories . and .. or system files */
  1398.     while (f->file.spec[0] == '.' || f->file.spec[0] != 0 &&
  1399.       f->file.spec[0] != DELETED_FILE && f->file.attribute & RDCF_SYSTEM)
  1400.     {
  1401.       if (++f->directory_index > 2) error_exit(f, RDCF_FILE_FORMAT_ERROR);
  1402.       read_directory_entry(f);
  1403.     }
  1404.     if (f->file.spec[0] == 0) return 0;
  1405.     first_index = f->directory_index;
  1406.     number_of_directory_entries = f->directory_cluster == 0 ?
  1407.       (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR :
  1408.       ENTRIES_PER_SECTOR*f->sectors_per_cluster;
  1409.     while (sorting)
  1410.     {
  1411.       struct directory *d, previous_entry;
  1412.       unsigned previous_directory_cluster;
  1413.       unsigned previous_directory_index;
  1414.       f->directory_index = first_index;
  1415.       f->directory_cluster = f->directory_first_cluster;
  1416.       sorting = 0;
  1417.       d = find_directory(f);
  1418.       while (1)
  1419.       {
  1420.         int comparison;
  1421.         previous_entry = *d;
  1422.         previous_directory_cluster = f->directory_cluster;
  1423.         previous_directory_index = f->directory_index;
  1424.         if (++f->directory_index == number_of_directory_entries)
  1425.         {
  1426.           if (f->directory_cluster == 0) break;
  1427.           {
  1428.             unsigned x = FAT_entry(f, f->directory_cluster);
  1429.             if (x >= f->last_cluster_mark) break;
  1430.             f->directory_cluster = x;
  1431.             f->directory_index = 0;
  1432.           }
  1433.         }
  1434.         d = find_directory(f);
  1435.         if (d->name_extension[0] == 0) break;
  1436.         comparison = entry_class(d) - entry_class(&previous_entry);
  1437.         if (comparison == 0)
  1438.         {
  1439.           switch (mode & RDCF_REVERSE-1)
  1440.           {
  1441.             case RDCF_EXTENSION_NAME:
  1442.               comparison =
  1443.                 memcmp(d->name_extension+NAME_SIZE,
  1444.                 previous_entry.name_extension+NAME_SIZE, EXTENSION_SIZE);
  1445.               if (comparison != 0) break;
  1446.               /* else fall through to case RDCF_NAME_EXTENSION */
  1447.             case RDCF_NAME_EXTENSION:
  1448.               comparison =
  1449.                 memcmp(d->name_extension, previous_entry.name_extension,
  1450.                 NAME_SIZE+EXTENSION_SIZE);
  1451.               break;
  1452.             case RDCF_DATE_TIME:
  1453.               comparison =
  1454.                 compare_four_bytes((unsigned char *)(&d->time),
  1455.                 (unsigned char *)(&previous_entry.time));
  1456.               break;
  1457.             case RDCF_SIZE:
  1458.               comparison =
  1459.                 compare_four_bytes((unsigned char *)(&d->size),
  1460.                 (unsigned char *)(&previous_entry.size));
  1461.               break;
  1462.           }
  1463.           if (mode & RDCF_REVERSE) comparison = -comparison;
  1464.         }
  1465.         if (comparison < 0)
  1466.         {
  1467.           struct directory this_entry = *d;
  1468.           unsigned this_directory_cluster = f->directory_cluster;
  1469.           unsigned this_directory_index = f->directory_index;
  1470.           f->directory_cluster = previous_directory_cluster;
  1471.           f->directory_index = previous_directory_index;
  1472.           d = find_directory(f);
  1473.           *d = this_entry;
  1474.           f->buffer_status = DIRTY;
  1475.           f->directory_cluster = this_directory_cluster;
  1476.           f->directory_index = this_directory_index;
  1477.           d = find_directory(f);
  1478.           *d = previous_entry;
  1479.           f->buffer_status = DIRTY;
  1480.           sorting = 1;
  1481.         }
  1482.       }
  1483.     }
  1484.     flush_buffer(f);
  1485.     return 0;
  1486.   }
  1487.  
  1488. #endif
  1489.  
  1490. #ifndef RDCF_SYSTEM_READ_ONLY
  1491.  
  1492.   int rdcf_undelete(struct rdcf *f, char *spec)
  1493.   {
  1494.     unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
  1495.     int first_character;
  1496.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1497.     spec = initialize_fcb(f, spec);
  1498.     if (find_file(f, spec))
  1499.     {
  1500.       if (f->file.attribute & RDCF_DIRECTORY)
  1501.         error_exit(f, RDCF_DIRECTORY_CONFLICT);
  1502.       error_exit(f, RDCF_FILE_NOT_FOUND);
  1503.     }
  1504.     first_character = f->file.spec[0];
  1505.     spec = spec_to_name_extension(f, name_extension, f->file.spec);
  1506.     name_extension[0] = DELETED_FILE;
  1507.     f->directory_cluster = f->directory_first_cluster;
  1508.     if (!find_file_in_directory_or_find_volume(f, name_extension) ||
  1509.       f->file.attribute & RDCF_VOLUME+RDCF_DIRECTORY)
  1510.     {
  1511.       error_exit(f, RDCF_FILE_NOT_FOUND);
  1512.     }
  1513.     f->position = 0;
  1514.     if (f->file.size != 0)
  1515.     {
  1516.       unsigned cluster_size = SECTOR_SIZE * f->sectors_per_cluster;
  1517.       unsigned long recovered_size = cluster_size;
  1518.       unsigned cluster = f->file.first_cluster;
  1519.       if (FAT_entry(f, cluster) != EMPTY_CLUSTER)
  1520.         error_exit(f, RDCF_UNRECOVERABLE_FILE);
  1521.       set_FAT_entry(f, cluster, f->last_cluster_mark);
  1522.       f->file.spec[0] = first_character;
  1523.       {
  1524.         while (recovered_size < f->file.size &&
  1525.           cluster < f->maximum_cluster_number)
  1526.         {
  1527.           cluster = add_new_cluster(f, cluster, cluster+1);
  1528.           if (cluster == EMPTY_CLUSTER)
  1529.           {
  1530.             f->position = f->file.size - recovered_size;
  1531.             f->file.size = recovered_size;
  1532.             break;
  1533.           }
  1534.           recovered_size += cluster_size;
  1535.         }
  1536.       }
  1537.     }
  1538.     f->file.spec[0] = first_character;
  1539.     update_directory_entry(f, 0);
  1540.     flush_buffer(f);
  1541.     return 0;
  1542.   }
  1543.  
  1544. #endif
  1545.  
  1546. #ifndef RDCF_SYSTEM_READ_ONLY
  1547.  
  1548.   int rdcf_wipe_drive(struct rdcf *f
  1549.     #ifdef RDCF_MULTIPLE_DRIVE
  1550.       , char *spec
  1551.     #endif
  1552.     )
  1553.   {
  1554.     unsigned cluster;
  1555.     if ((f->result=setjmp(f->error)) != 0) return (long)(f->result);
  1556.     #ifndef RDCF_MULTIPLE_DRIVE
  1557.       initialize_fcb(f, NULL);
  1558.     #else
  1559.       if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
  1560.     #endif
  1561.     for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++)
  1562.     {
  1563.       if (FAT_entry(f, cluster) == EMPTY_CLUSTER)
  1564.       {
  1565.         unsigned sector = first_sector_in_cluster(f, cluster);
  1566.         unsigned count = f->sectors_per_cluster;
  1567.         do write_sector(f, sector++, f->buffer); while (--count != 0);
  1568.       }
  1569.     }
  1570.     return 0;
  1571.   }
  1572.  
  1573. #endif
  1574.  
  1575. #ifndef RDCF_SYSTEM_READ_ONLY
  1576.  
  1577.   int rdcf_write(struct rdcf *f, void *buf, int count)
  1578.   {
  1579.     unsigned long size = f->file.size;
  1580.     unsigned long position = f->position;
  1581.     unsigned unwritten_bytes = count;
  1582.     unsigned first_possibly_empty_cluster = 2;
  1583.     char *buffer = buf;
  1584.     if ((f->result=setjmp(f->error)) != 0) return f->result;
  1585.     f->buffer_status = EMPTY;
  1586.     if (f->mode == RDCF_READ) error_exit(f,RDCF_MODE_ERROR);
  1587.     while (unwritten_bytes>0)
  1588.     {
  1589.       unsigned sector;
  1590.       unsigned n = unwritten_bytes;
  1591.       unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE;
  1592.       if (n>rest_of_sector) n = rest_of_sector;
  1593.       if (f->cluster == EMPTY_CLUSTER || f->cluster >= f->last_cluster_mark)
  1594.       {
  1595.         unsigned new_cluster =
  1596.           add_new_cluster(f, f->last_cluster, first_possibly_empty_cluster);
  1597.         if (new_cluster == EMPTY_CLUSTER) break;
  1598.         first_possibly_empty_cluster = new_cluster+1;
  1599.         f->cluster = f->last_cluster = new_cluster;
  1600.         if (f->file.first_cluster==EMPTY_CLUSTER)
  1601.           f->file.first_cluster = new_cluster;
  1602.       }
  1603.       sector = first_sector_in_cluster(f, f->cluster) +
  1604.           (position/SECTOR_SIZE)%f->sectors_per_cluster;
  1605.       if (position%SECTOR_SIZE==0 &&
  1606.         (n==SECTOR_SIZE || position+n>=size))
  1607.       {
  1608.         write_sector(f, sector, buffer);
  1609.       }
  1610.       else /* write a partial sector */
  1611.       {
  1612.         read_buffer(f, sector);
  1613.         memcpy(f->buffer+position%SECTOR_SIZE, buffer, n);
  1614.         f->buffer_status = DIRTY;
  1615.       }
  1616.       buffer += n;
  1617.       position += n;
  1618.       unwritten_bytes -= n;
  1619.       if (position>size) size = position;
  1620.       if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0)
  1621.       {
  1622.         unsigned next_cluster = FAT_entry(f, f->cluster);
  1623.         if (next_cluster >= f->last_cluster_mark) f->last_cluster = f->cluster;
  1624.         f->cluster = next_cluster;
  1625.       }
  1626.     }
  1627.     flush_buffer(f);
  1628.     f->position = position;
  1629.     f->file.size = size;
  1630.     f->mode |= WRITTEN;
  1631.     return f->result = count-unwritten_bytes;
  1632.   }
  1633.  
  1634. #endif
  1635.  
  1636.